package org.coody.datafuzzer.rute;

import java.io.File;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.coody.datafuzzer.instance.TableInstance;
import org.coody.framework.core.build.ConfigBuilder;
import org.coody.framework.core.util.PrintException;
import org.coody.framework.core.util.PropertUtil;
import org.coody.framework.core.util.StringUtil;
import org.coody.framework.jdbc.JdbcHandle;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class Rute {

	static Logger logger = Logger.getLogger(Rute.class);

	public static void main(String[] args) throws Exception {

		String config="config/config.properties";
		File file=new File(config);
		ConfigBuilder configBuilder = new ConfigBuilder("config");
		Method method=PropertUtil.getTargeMethod(ConfigBuilder.class.getDeclaredMethods(), "loadPropertByDir", File.class);
		method.setAccessible(true);
		method.invoke(configBuilder, file);
		// 获得主库
		String core = System.getProperty("db.core.alias");
		if (StringUtil.isNullOrEmpty(core)) {
			logger.info("未配置主库>>db.core.alias");
			return;
		}
		logger.info("主库>>" + core.trim());
		JdbcHandle coreJdbcHandle = makeJdbcHandle(core);
		// 获得从库列表
		String slaves = System.getProperty("db.slave.alias");
		if (StringUtil.isNullOrEmpty(core)) {
			logger.info("未配置从库>>db.slave.alias");
			return;
		}
		Map<String, JdbcHandle> jdbcMap = new HashMap<String, JdbcHandle>();
		String[] slaveAlias = slaves.split(",");
		for (String slave : slaveAlias) {
			JdbcHandle jdbcHandle = makeJdbcHandle(slave);
			logger.info("从库>>" + slave.trim());
			jdbcMap.put(slave.trim(), jdbcHandle);
		}
		if (StringUtil.isNullOrEmpty(jdbcMap)) {
			logger.info("从库列表初始化失败>>db.slave.alias");
			return;
		}
		// 获得主库表列表
		TableInstance tableInstance = new TableInstance(coreJdbcHandle);
		String tableConf = System.getProperty("db.table.list");
		Set<String> tables = null;
		if (!StringUtil.isNullOrEmpty(tableConf)) {
			tables = new HashSet<String>(StringUtil.splitByMosaic(tableConf, ","));
		}
		if (StringUtil.isNullOrEmpty(tables)) {
			tables = tableInstance.getTables();
		}
		if (StringUtil.isNullOrEmpty(tables)) {
			logger.info("主库表列表为空");
			return;
		}
		logger.info("数据表列表(" + tables.size() + ")：");
		for (String table : tables) {
			logger.info("\t" + table);
		}
		// 获得屏蔽表列表
		String tableUnable = System.getProperty("db.table.unable");
		Set<String> unables = new HashSet<String>();
		if (!StringUtil.isNullOrEmpty(tableUnable)) {
			unables = new HashSet<String>(StringUtil.splitByMosaic(tableUnable, ","));
		}
		Integer pageSize = StringUtil.toInteger(System.getProperty("db.table.subsection"));
		// 比较数据
		for (String table : tables) {
			if (unables.contains(table)) {
				logger.info("该表已屏蔽检索>>" + table);
				continue;
			}
			// 获得表主键
			List<String> primarys = coreJdbcHandle.getPrimaryKeys(table);
			if (StringUtil.isNullOrEmpty(primarys)) {
				logger.info("该表不存在主键，不参与比较>>" + table);
				continue;
			}
			/**
			 * TO DO 数据比较
			 */
			logger.info("比较数据>>" + table);
			Integer page = 1;
			List<Map<String, Object>> data = getPager(coreJdbcHandle, table, primarys, page, pageSize);
			while (!StringUtil.isNullOrEmpty(data)) {
				try {
					logger.info("页码：>>" + page);
					for (Map<String, Object> line : data) {
						Map<String, Object> params = new HashMap<String,Object>();
						for (String dbkey : primarys) {
							params.put(dbkey, line.get(dbkey));
						}
						for (String slave : jdbcMap.keySet()) {
							JdbcHandle slaveJdbcHandle = jdbcMap.get(slave);
							Map<String, Object> slaveLine = getLine(slaveJdbcHandle, table, params);
							if (StringUtil.isNullOrEmpty(slaveLine)) {
								logger.info("\t数据不一致：(" + slave + ")库不存在该数据>>" + buildSql(tableUnable, params));
								continue;
							}
							for (String key : line.keySet()) {
								if (!slaveLine.containsKey(key)) {
									logger.info("\t数据不一致：(" + slave + ")库不存该数据不存在字段(" + key + ")>>"
											+ buildSql(tableUnable, params));
									continue;
								}
								Object coreValue = line.get(key);
								Object slaveValue = slaveLine.get(key);
								if (StringUtil.isNullOrEmpty(coreValue) && StringUtil.isNullOrEmpty(slaveValue)) {
									continue;
								}
								if (coreValue == null) {
									coreValue = "";
								}
								if (!coreValue.equals(slaveValue)) {
									logger.info("\t数据不一致：(" + slave + ")库不存该数据字段不对应(" + key + ")>>"
											+ buildSql(tableUnable, params));
									continue;
								}
							}
						}
					}
				} finally {
					page++;
					data = getPager(coreJdbcHandle, table, primarys, page, pageSize);
				}
			}
			logger.info("比较数据完成>>" + table);
		}
	}

	private static String buildSql(String table, Map<String, Object> params) {
		StringBuilder sql = new StringBuilder(MessageFormat.format("select * from {0} where 1=1 ", table));
		for (String field : params.keySet()) {
			Object value = params.get(field);
			sql.append(MessageFormat.format(" and {0}", field));
			if (StringUtil.isNullOrEmpty(value)) {
				sql.append(" is null");
				continue;
			}
			if (value instanceof Number) {
				sql.append(" = " + value);
				continue;
			}
			sql.append(" = '" + value.toString() + "'");
		}
		return sql.toString();
	}

	private static Map<String, Object> getLine(JdbcHandle jdbcHandle, String table, Map<String, Object> params) {
		StringBuilder sql = new StringBuilder(MessageFormat.format("select * from {0} where 1=1 ", table));
		List<Object> queryParams = new ArrayList<Object>();
		for (String field : params.keySet()) {
			Object value = params.get(field);
			sql.append(MessageFormat.format(" and {0}", field));
			if (StringUtil.isNullOrEmpty(value)) {
				sql.append(" is null");
				continue;
			}
			sql.append(" = ?");
			queryParams.add(value);
		}
		return jdbcHandle.queryFirst(sql.toString(), queryParams.toArray());
	}

	private static List<Map<String, Object>> getPager(JdbcHandle jdbcHandle, String table, List<String> primarys,
			Integer page, Integer pageSize) {
		String sql = MessageFormat.format("select * from {0} order by {1} limit {2},{3}", table,
				StringUtil.collectionMosaic(primarys, ","), String.valueOf((page - 1) * pageSize), String.valueOf(pageSize));
		return jdbcHandle.query(sql);
	}

	private static JdbcHandle makeJdbcHandle(String alias) throws Exception {
		try {
			alias = alias.trim();
			HikariConfig config = new HikariConfig();
			config.setJdbcUrl(System.getProperty(alias + ".jdbc.url"));
			config.setUsername(System.getProperty(alias + ".jdbc.user"));
			config.setPassword(System.getProperty(alias + ".jdbc.password"));
			config.setDriverClassName(System.getProperty(alias + ".jdbc.driverClass"));
			config.addDataSourceProperty("cachePrepStmts", true);
			config.addDataSourceProperty("prepStmtCacheSize", 500);
			config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
			config.setConnectionTestQuery("SELECT 1");
			config.setMinimumIdle(StringUtil.toInteger(System.getProperty("jdbc.minimumIdle")));
			config.setMaximumPoolSize(StringUtil.toInteger(System.getProperty("jdbc.maximumPoolSize")));
			HikariDataSource dataSource = new HikariDataSource(config);
			JdbcHandle jdbcHandle = new JdbcHandle();
			jdbcHandle.setDataSource(dataSource);
			return jdbcHandle;
		} catch (Exception e) {
			logger.info(PrintException.getErrorStack(e));
			logger.info("数据库连接失败>>" + alias);
			throw e;
		}
	}
}
